package org.iori.util.text;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * The class for opreate Text file.
 * <p>
 * The Text file may include blank lines which only includes whitespace
 * character(s).
 * <p>
 * Each line with a line number, based 0 . The getLine() method can return the
 * string of each line.
 * <p>
 * The text file may be encoded with specified charset, default value is "SJIS"
 * for japanese, use setFileEncoding() to change to others.
 * 
 * @author zhang_jianbin
 * @history
 *         <p>
 *         2006-3-30 Created Zhang Jianbin
 */
public class TXTFileAccessor {
	private Log log = LogFactory.getLog(TXTFileAccessor.class);

	/**
	 * store all the lines of the file
	 */
	protected List lines = new ArrayList();

	/**
	 * file encoding, default is SJIS
	 */
	protected String fileEncoding = "SJIS";

	/**
	 * csv filename
	 */
	protected String filename;

	/**
	 * Constructs a TXTFileAccessor object
	 * 
	 */
	public TXTFileAccessor() {

	}

	/**
	 * Constructor
	 * 
	 * @param filename
	 *            the text filename
	 */
	public TXTFileAccessor(String filename) {
		this.filename = filename;
	}

	/**
	 * Returns all the lines in the text file.
	 * 
	 * @throws FileNotFoundException
	 *             if the text file is not found
	 * @throws IOException
	 *             if an I/O error ocuurs
	 */
	public void read() throws FileNotFoundException, IOException {
		read(new FileInputStream(filename));
	}

	/**
	 * Returns all the lines in the text file.
	 * 
	 * @throws IOException
	 *             if an I/O error ocuurs
	 */
	public void read(InputStream is) throws IOException {
		lines.clear();
		LineNumberReader reader = new LineNumberReader(new InputStreamReader(
				is, fileEncoding));

		String line;
		long i = 0;
		while (null != (line = reader.readLine())) {
			if ("UTF-8".equals(fileEncoding.toUpperCase()) && 0 == i
					&& line.length() > 0 && 0XFEFF == line.charAt(0)) {
				line = line.substring(1);
			}
			lines.add(line);
			i++;
		}

		// close the file
		reader.close();

		if (log.isDebugEnabled()) {
			log.debug("Read " + i + " lines from the file " + filename);
		}
	}

	/**
	 * Returns the line string
	 * 
	 * @param lineNumber
	 *            the line number in the CSV file, based 0
	 * @return the string of line
	 */
	public String getLine(int lineNumber) {
		return (String) lines.get(lineNumber);
	}

	/**
	 * Sets the string value to the specified line. The data don't be wrote into
	 * file untile call writeback().
	 * 
	 * @param lineNumber
	 *            the line number based 0
	 * @param str
	 *            the string value
	 */
	public void setLine(int lineNumber, String str) {
		lines.set(lineNumber, str);
	}

	/**
	 * Removes the specified line. This line don't be remove from file untile
	 * call writeback().
	 * 
	 * @param lineNumber
	 *            the line number based 0
	 */
	public void removeLine(int lineNumber) {
		lines.remove(lineNumber);
	}

	/**
	 * Appends a new line at the end. The new line is initialized empty string.
	 * 
	 * @return the new line number base 0
	 */
	public int appendLine() {
		lines.add("");

		return lines.size() - 1;
	}

	/**
	 * Writes the lines back to the file.
	 * 
	 * @throws IOException
	 *             if an I/O error ocuurs
	 */
	public void writeback() throws IOException {
		OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(
				filename), fileEncoding);

		// write to the file
		for (int i = 0; i < lines.size(); i++) {
			osw.write((String) lines.get(i));
			if (i != lines.size() - 1) {
				osw.write("\r\n");
			}
		}

		// close the file.
		osw.close();
	}

	/**
	 * Writes the lines back to the file.
	 * 
	 * @param filename
	 *            the new filename to save
	 * @param fileEncoding
	 *            the encoding
	 * @throws IOException
	 *             if error occurs
	 */
	public void saveAs(String filename, String fileEncoding) throws IOException {
		OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(
				filename), fileEncoding);

		// write to the file
		for (int i = 0; i < lines.size(); i++) {
			osw.write((String) lines.get(i));
			if (i != lines.size() - 1) {
				osw.write("\r\n");
			}
		}

		// close the file.
		osw.close();
	}

	/**
	 * Determines whether the line is blank line
	 * 
	 * @param lineNumber
	 *            the line number in the file, based 0
	 * @return true if the characters in the line are only whitespace
	 *         character(space, \t,etc.), false otherwise.
	 */
	public boolean isBlankLine(int lineNumber) {
		return getLine(lineNumber).trim().equals("");
	}

	/**
	 * Returns the file encoding used to read() and writeback() method
	 * 
	 * @return Returns the fileEncoding.
	 */
	public String getFileEncoding() {
		return fileEncoding;
	}

	/**
	 * Sets the file encoding used to read() and writeback() method
	 * 
	 * @param fileEncoding
	 *            The fileEncoding to set.
	 */
	public void setFileEncoding(String fileEncoding) {
		this.fileEncoding = fileEncoding;
	}

	/**
	 * Returns the number of all the lines in the file.
	 * 
	 * @return the line count
	 */
	public int getLineCount() {
		return lines.size();
	}

	/**
	 * Returns the text contents of the file.
	 * 
	 * @return the String
	 */
	public String getText() {
		StringBuffer sb = new StringBuffer(1024);
		for (int i = 0; i < lines.size(); i++) {
			sb.append((String) lines.get(i));
			if (lines.size() - 1 != i) {
				sb.append("\r\n");
			}
		}

		return sb.toString();
	}

	/**
	 * Changes the file text content but don't writeback immediately, and the
	 * number of the lines will be one only.
	 * 
	 * @param text
	 *            the text string
	 */
	public void setText(String text) {
		if (null == text) {
			return;
		}

		lines.clear();
		lines.add(text);
	}
}